Src |
CREATE FUNCTION "URX_PUBLIC"."_st_aspect4ma"(value double precision[], pos integer[], userargs text[])
RETURNS double precision AS
$BODY$
DECLARE
x integer;
y integer;
z integer;
_width double precision;
_height double precision;
_units text;
dz_dx double precision;
dz_dy double precision;
aspect double precision;
halfpi double precision;
_value double precision[][][];
ndims int;
BEGIN
ndims := array_ndims(value);
-- add a third dimension if 2-dimension
IF ndims = 2 THEN
_value := "URX_PUBLIC"._ST_convertarray4ma(value);
ELSEIF ndims != 3 THEN
RAISE EXCEPTION 'First parameter of function must be a 3-dimension array';
ELSE
_value := value;
END IF;
IF (
array_lower(_value, 2) != 1 OR array_upper(_value, 2) != 3 OR
array_lower(_value, 3) != 1 OR array_upper(_value, 3) != 3
) THEN
RAISE EXCEPTION 'First parameter of function must be a 1x3x3 array with each of the lower bounds starting from 1';
END IF;
IF array_length(userargs, 1) < 3 THEN
RAISE EXCEPTION 'At least three elements must be provided for the third parameter';
END IF;
-- only use the first raster passed to this function
IF array_length(_value, 1) > 1 THEN
RAISE NOTICE 'Only using the values from the first raster';
END IF;
z := array_lower(_value, 1);
_width := userargs[1]::double precision;
_height := userargs[2]::double precision;
_units := userargs[3];
-- check that center pixel isn't NODATA
IF _value[z][2][2] IS NULL THEN
RETURN NULL;
-- substitute center pixel for any neighbor pixels that are NODATA
ELSE
FOR y IN 1..3 LOOP
FOR x IN 1..3 LOOP
IF _value[z][y][x] IS NULL THEN
_value[z][y][x] = _value[z][2][2];
END IF;
END LOOP;
END LOOP;
END IF;
dz_dy := ((_value[z][3][1] + _value[z][3][2] + _value[z][3][2] + _value[z][3][3]) -
(_value[z][1][1] + _value[z][1][2] + _value[z][1][2] + _value[z][1][3]));
dz_dx := ((_value[z][1][3] + _value[z][2][3] + _value[z][2][3] + _value[z][3][3]) -
(_value[z][1][1] + _value[z][2][1] + _value[z][2][1] + _value[z][3][1]));
-- aspect is flat
IF abs(dz_dx) = 0::double precision AND abs(dz_dy) = 0::double precision THEN
RETURN -1;
END IF;
-- aspect is in radians
aspect := atan2(dz_dy, -dz_dx);
-- north = 0, pi/2 = east, 3pi/2 = west
halfpi := pi() / 2.0;
IF aspect > halfpi THEN
aspect := (5.0 * halfpi) - aspect;
ELSE
aspect := halfpi - aspect;
END IF;
IF aspect = 2 * pi() THEN
aspect := 0.;
END IF;
-- output depends on user preference
CASE substring(upper(trim(leading from _units)) for 3)
-- radians
WHEN 'rad' THEN
RETURN aspect;
-- degrees (default)
ELSE
RETURN degrees(aspect);
END CASE;
END;
$BODY$
LANGUAGE 'plpgsql' IMMUTABLE;
|